X-ASR
先发 start,把 16kHz 的 PCM 切成小块逐块发送,最后发 end,服务端返回最终文本。它按 1 秒为一窗出结果——这个设定,正是下面「补静音」细节的来历。
Capstone Project · 压轴项目
教程里学过的环节,在这里各就各位。这是真实项目 simple_X_Translator 的完整拆解:按住按钮说一句中文,两三秒后,它用你自己的音色说出英语、日语或法语。
Pipeline · 全链路
点「走一遍」,看一句中文从声音变成文字、换一种语言、再变回声音;每个节点可点开看细节。
Services · 三个 AI 服务
编排服务只做调度,把三个独立服务串成一条流水线。
先发 start,把 16kHz 的 PCM 切成小块逐块发送,最后发 end,服务端返回最终文本。它按 1 秒为一窗出结果——这个设定,正是下面「补静音」细节的来历。
一个 OpenAI 接口兼容的翻译模型,没有专用协议:把识别出的中文放进 messages,靠 system prompt 约定行为。prompt 写得越死,输出越干净。
串场角色:把 ASR 的输出,变成 TTS 的输入
给它两样东西:要说的文本 + 一段参考音频。它把参考音频里的音色“穿”到文本上——参考给谁的声音,说出来就像谁。这个项目递过去的参考,就是你刚才的录音。
Engineering · 工程细节
真实项目和课堂例子之间,差的往往就是这几行代码。
最后 0.4 秒的字,去哪了?X-ASR 攒够 1 秒音频才出一次结果。你说了 3.4 秒:前 3 秒被完整识别,最后 0.4 秒凑不满一窗——直接被丢掉。解法很简单:发送前在 PCM 末尾拼上 1 秒全零采样。真正的句尾,就这样被“推”进了完整窗口。
多补的 0.4 秒静音落在第 4 窗之外,被丢掉也无所谓——那里本来就没有话。
# pipeline.py · 细节一:末尾补 1 秒静音 tail = int(sample_rate * 1.0) * 2 # 1 秒 × 16bit(2 字节/采样) pcm_bytes = pcm_bytes + b"\x00" * tail # 把最后几个字“推”进完整识别窗口
凭什么合成的外语,听起来像“你”在说?X-Voice 是克隆音色的 TTS,每次请求必须带一段参考音频。不带,它就不知道“用谁的声音说”。项目把你这次的录音直接复用为参考——一份录音,两个用途。译文听起来就像你在说外语。
# pipeline.py · 细节二:用本次录音做音色参考 payload = { "text": translated_text, # 翻译后的外语文本 "audio_paths": [clone_audio_path],# ← 你刚才的录音,成为“你的音色” "language": target_lang, }
Live Demo · 在线试驾
这台翻译机的编排后端(xtranslate)和三个 AI 服务都跑在课程 GPU 服务器上,浏览器通过 WebSocket 流式协议直连编排服务。状态灯全绿时可直接试用;否则看录播演示。
按住说、松开发送;也可以点一下开始、再点一下结束。录音最长 10 秒。后端地址留空即自动直连(线上走本站通道,本机调试走 http://localhost:7654 隧道),填了自定义地址则只发往你填的后端。浏览器录音要求页面运行在 localhost 或 HTTPS 下。
不知道说什么?试试:“帮我把窗户打开”,或者“这道题真的只有一种解法吗”。
asr_text = "帮我把窗户打开"
X-ASR 流式返回识别文本;句尾那个“开”字,靠补静音才保住。
system: 只输出译文,不解释
translated_text =
"Could you open the window?"
HY-MT 走 chat 接口一问一答;prompt 管住它只输出译文。
<binary> ← 48kHz PCM16 语音流
合成语音以 WebSocket 二进制帧流式返回,拼起来就是一段 48kHz 音频。想听“自己的”真声,请建隧道试驾。
# 一次往返的 WebSocket 消息流(录播示例,字段与真实协议一致) connect ws://localhost:7654/ws → {"action":"session_config", "target_lang":"en", "sample_rate":16000} → {"event":"start"} {"event":"config_audio"} {"event":"turn_started"} → <binary> 16kHz PCM16 × N 帧(你的录音) → {"event":"stop"} ← {"action":"asr_result", "data":{"type":"partial", "text":"帮我把窗户"}} ← {"action":"asr_result", "data":{"type":"final", "text":"帮我把窗户打开"}} ← {"action":"translation_ready", "data":{"translated_text":"Could you open the window?"}} ← <binary> 48kHz PCM16 × M 帧(合成语音,你的音色)
DIY · 自己跑起来
服务已在课程 GPU 服务器常驻;本机只需一条 SSH 隧道,把 7654 端口映射过来。
# 课程 GPU 服务器上已在 tmux 里常驻三件套 + 编排服务: # HY-MT(翻译,:8000)· X-Voice(合成,:11998)· SenseVoice(识别,进程内) # xtranslate 编排服务 → http://0.0.0.0:7654(WebSocket 入口 /ws) # 本机只需一条 SSH 隧道,把 7654 映射到 localhost: ssh -L 7654:localhost:7654 -p 2222 <账号>@ssh.zw1.paratera.com curl http://localhost:7654/healthz # → {"status":"ok"} ——翻译机活了