diff --git a/app.py b/app.py index bc53ba6b..9679921b 100644 --- a/app.py +++ b/app.py @@ -4,13 +4,14 @@ from config.settings import load_config, check_config_file from core.websocket_server import WebSocketServer from manager.http_server import WebUI from aiohttp import web -from core.utils.util import get_local_ip +from core.utils.util import get_local_ip, check_ffmpeg_installed TAG = __name__ async def main(): check_config_file() + check_ffmpeg_installed() logger = setup_logging() config = load_config() diff --git a/core/handle/sendAudioHandle.py b/core/handle/sendAudioHandle.py index ab653a31..07b24fb9 100644 --- a/core/handle/sendAudioHandle.py +++ b/core/handle/sendAudioHandle.py @@ -16,29 +16,37 @@ async def isLLMWantToFinish(last_text): async def sendAudioMessage(conn, audios, text): - # 发送 tts.start + # 发送句子开始消息 if text == conn.tts_first_text: logger.bind(tag=TAG).info(f"发送第一段语音: {text}") - conn.tts_start_speak_time = time.time() + conn.tts_start_speak_time = time.perf_counter() await send_tts_message(conn, "sentence_start", text) - # 发送音频数据 - frame_duration = 60 # 初始帧持续时间(毫秒) - start_time = time.time() # 记录开始时间 - for idx, opus_packet in enumerate(audios): + # 初始化流控参数 + frame_duration = 60 # 毫秒 + start_time = time.perf_counter() # 使用高精度计时器 + play_position = 0 # 已播放的时长(毫秒) + + for opus_packet in audios: if conn.client_abort: return + # 计算当前包的预期发送时间 - expected_time = start_time + idx * (frame_duration / 1000) - current_time = time.time() - # 如果未到预期时间则等待差值 - if current_time < expected_time: - await asyncio.sleep(expected_time - current_time) + expected_time = start_time + (play_position / 1000) + current_time = time.perf_counter() + + # 等待直到预期时间 + delay = expected_time - current_time + if delay > 0: + await asyncio.sleep(delay) + # 发送音频包 await conn.websocket.send(opus_packet) - + play_position += frame_duration # 更新播放位置 + await send_tts_message(conn, "sentence_end", text) + # 发送结束消息(如果是最后一个文本) if conn.llm_finish_task and text == conn.tts_last_text: - await send_tts_message(conn, 'stop') + await send_tts_message(conn, 'stop', None) if await isLLMWantToFinish(text): await conn.close() diff --git a/core/utils/util.py b/core/utils/util.py index a61f770f..df792cbe 100644 --- a/core/utils/util.py +++ b/core/utils/util.py @@ -3,6 +3,7 @@ import re import json import yaml import socket +import subprocess def get_project_dir(): @@ -114,3 +115,29 @@ def check_password(password): # 如果满足所有条件,则返回True return True + +def check_ffmpeg_installed(): + ffmpeg_installed = False + try: + # 执行ffmpeg -version命令,并捕获输出 + result = subprocess.run( + ['ffmpeg', '-version'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + check=True # 如果返回码非零则抛出异常 + ) + # 检查输出中是否包含版本信息(可选) + output = result.stdout + result.stderr + if 'ffmpeg version' in output.lower(): + ffmpeg_installed = True + return False + except (subprocess.CalledProcessError, FileNotFoundError): + # 命令执行失败或未找到 + ffmpeg_installed = False + if not ffmpeg_installed: + error_msg = "您的电脑还没正确安装ffmpeg\n" + error_msg += "\n建议您:\n" + error_msg += "1、按照项目的安装文档,正确进入conda环境\n" + error_msg += "2、查阅安装文档,如何在conda环境中安装ffmpeg\n" + raise ValueError(error_msg) \ No newline at end of file diff --git a/docs/Deployment.md b/docs/Deployment.md index 4fc94d2d..e042ed38 100644 --- a/docs/Deployment.md +++ b/docs/Deployment.md @@ -159,14 +159,31 @@ poetry run python app.py 本项目使用`conda`管理依赖环境。如果不方便安装`conda`,需要根据实际的操作系统安装好`libopus`和`ffmpeg`。 如果确定使用`conda`,则安装好后,开始执行以下命令。 +重要提示!windows 用户,可以通过安装`Anaconda`来管理环境。安装好`Anaconda`后,在`开始`那里搜索`anaconda`相关的关键词, +找到`Anaconda Prpmpt`,使用管理员身份运行它。如下图。 + +![conda_prompt](./images/conda_env_1.png) + +运行之后,如果你能看到命令行窗口前面有一个(base)字样,说明你成功进入了`conda`环境。那么你就可以执行以下命令了。 + +![conda_env](./images/conda_env_2.png) + ``` conda remove -n xiaozhi-esp32-server --all -y conda create -n xiaozhi-esp32-server python=3.10 -y conda activate xiaozhi-esp32-server -conda install conda-forge::libopus -y -conda install conda-forge::ffmpeg -y + +# 添加清华源通道 +conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main +conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free +conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge + +conda install libopus -y +conda install ffmpeg -y ``` +请注意,以上命令,不是一股脑执行就成功的,你需要一步步执行,每一步执行完后,都检查一下输出的日志,查看是否成功。 + ## 2.安装本项目依赖 你先要下载本项目源码,源码可以通过`git clone`命令下载,如果你不熟悉`git clone`命令。 @@ -179,7 +196,7 @@ conda install conda-forge::ffmpeg -y 你需要把它重命名成`xiaozhi-esp32-server`,好了请记住这个目录,我们暂且称它为`项目目录`。 ``` -# 使用dos或者终端,进入到你的项目目录,执行以下命令 +# 继续使用conda环境,进入到你的项目目录,执行以下命令 conda activate xiaozhi-esp32-server pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ pip install -r requirements.txt diff --git a/docs/images/conda_env_1.png b/docs/images/conda_env_1.png new file mode 100644 index 00000000..114256a7 Binary files /dev/null and b/docs/images/conda_env_1.png differ diff --git a/docs/images/conda_env_2.png b/docs/images/conda_env_2.png new file mode 100644 index 00000000..61f4626e Binary files /dev/null and b/docs/images/conda_env_2.png differ